package com.gitee.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gitee.bo.OrderBo;
import com.gitee.client.CartClient;
import com.gitee.client.CookbookClient;
import com.gitee.client.CookbookFoodClient;
import com.gitee.enums.OrderStatusEnum;
import com.gitee.enums.ResponseEnum;
import com.gitee.exception.OrderException;
import com.gitee.mapper.OrderMapper;
import com.gitee.pojo.Order;
import com.gitee.pojo.OrderItem;
import com.gitee.sdk.CartSDK;
import com.gitee.sdk.CookbookSDK;
import com.gitee.sdk.FoodVo;
import com.gitee.service.IOrderItemService;
import com.gitee.service.IOrderService;
import com.gitee.vo.OrderVo;
import com.gitee.vo.PageVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 订单管理
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {

    @Autowired
    private CookbookClient cookbookClient;

    @Autowired
    private CookbookFoodClient cookbookFoodClient;

    @Autowired
    private CartClient cartClient;

    @Autowired
    private IOrderItemService orderItemService;

    @Autowired
    private OrderMapper orderMapper;


    /**
     * 订单查询
     *
     * @param cookbookType
     * @param orderStatus
     * @param orderType
     * @param userId
     * @param pageNum
     * @param pageSize
     * @return
     */
    @Override
    public PageVo<OrderVo> pageOrderVos(Integer cookbookType, Integer orderStatus, Integer orderType,
                                        Integer userId, Integer pageNum, Integer pageSize) {
        List<OrderVo> orderVos = orderMapper.selectOrderVos((pageNum - 1) * pageSize, pageSize, cookbookType, orderStatus, orderType, userId);
        LambdaQueryWrapper<Order> queryWrapper = new QueryWrapper<Order>().lambda()
                .eq(cookbookType != null, Order::getCookbookType, cookbookType)
                .eq(orderStatus != null, Order::getOrderStatus, orderStatus)
                .eq(orderType != null, Order::getOrderType, orderType)
                .eq(userId != null, Order::getUserId, userId);
        Integer total = super.count(queryWrapper);
        return new PageVo<>(orderVos, total);
    }

    /**
     * 删除订单
     *
     * @param orderId
     * @param userId
     * @return
     */
    @Transactional
    @Override
    public Boolean deleteOrder(Integer orderId, Integer userId) {
        Order order = super.getById(orderId);
        if (order == null || !order.getUserId().equals(userId)) throw new OrderException(ResponseEnum.NO_ORDER);
        if (OrderStatusEnum.of(order.getOrderStatus()) != OrderStatusEnum.CANCELLED) throw new OrderException(ResponseEnum.ORDER_STATUS_ERROR);
        return super.removeById(orderId);
    }

    /**
     * 取消订单
     *
     * @param orderId
     * @param userId
     * @return
     */
    @Transactional
    @Override
    public Boolean cancelOrder(Integer orderId, Integer userId) {
        Order order = super.getById(orderId);
        if (order == null || !order.getUserId().equals(userId)) throw new OrderException(ResponseEnum.NO_ORDER);
        if (OrderStatusEnum.of(order.getOrderStatus()) != OrderStatusEnum.COMMITTED) throw new OrderException(ResponseEnum.ORDER_STATUS_ERROR);
        order.setOrderStatus(OrderStatusEnum.CANCELLED.getCode());
        return super.updateById(order);
    }

    /**
     * 创建订单
     *
     * @param userId
     * @param orderBo
     * @return
     */
    @Transactional
    @Override
    public Boolean createOrder(Integer userId, OrderBo orderBo) {
        CookbookSDK cookbookVo = cookbookClient.getTodayCookbook(orderBo.getCookbookType());
        //获取购物车
        List<CartSDK> carts = cartClient.listCarts(userId, cookbookVo.getId(), orderBo.getCookbookType()).stream().filter(CartSDK::getSelected).collect(Collectors.toList());
        if (CollectionUtil.isEmpty(carts)) {
            throw new OrderException(ResponseEnum.NO_CART_ORDER);
        }
        //校验是否超过菜品预订上限
        Optional<Integer> totalQuantityOptional = carts.stream().map(CartSDK::getQuantity).reduce(Integer::sum);
        if (!totalQuantityOptional.isPresent()) {
            throw new OrderException(ResponseEnum.NO_CART_ORDER);
        }
        Integer totalQuantity = totalQuantityOptional.get();
        if (totalQuantity > cookbookVo.getMaxNum()) {
            throw new OrderException(ResponseEnum.ORDER_FOOD_MAX);
        }
        //校验菜品库存
        Map<Integer, FoodVo> foodVoMap = cookbookVo.getFoodVos().stream().collect(Collectors.toMap(FoodVo::getId, foodVo -> foodVo));
        for (CartSDK cart : carts) {
            FoodVo foodVo = foodVoMap.get(cart.getFoodId());
            if (foodVo == null) throw new OrderException(ResponseEnum.NO_FOOD);
            if (foodVo.getStock() < cart.getQuantity()) throw new OrderException(ResponseEnum.NO_STOCK);
        }
        //生成订单
        Order order = buildOrder(carts, foodVoMap, cookbookVo, orderBo, userId);
        super.save(order);
        //生成订单详情
        List<OrderItem> orderItems = buildOrderItems(carts, foodVoMap, order);
        orderItemService.saveBatch(orderItems);
        //减库存
        for (CartSDK cart : carts) {
            FoodVo foodVo = foodVoMap.get(cart.getFoodId());
            Integer stock = foodVo.getStock() - cart.getQuantity();
            cookbookFoodClient.updateByCookbookIdAndFoodId(cookbookVo.getId(), foodVo.getId(), stock);
        }
        //更新购物车
        for (CartSDK cart : carts) {
            cartClient.removeCart(userId, cart.getFoodId(), cookbookVo.getId(), cookbookVo.getCookbookType());
        }
        //发起支付 TODO
        return true;
    }

    private List<OrderItem> buildOrderItems(List<CartSDK> carts, Map<Integer, FoodVo> foodVoMap, Order order) {
        List<OrderItem> orderItems = new ArrayList<>();
        for (CartSDK cart : carts) {
            OrderItem orderItem = new OrderItem();
            FoodVo foodVo = foodVoMap.get(cart.getFoodId());
            orderItem.setFoodId(foodVo.getId());
            orderItem.setFoodTitle(foodVo.getTitle());
            orderItem.setFoodDesc(foodVo.getDescription());
            orderItem.setFoodImg(foodVo.getImg());
            orderItem.setFoodNum(cart.getQuantity());
            orderItem.setFoodUnitPrice(foodVo.getPrice());
            orderItem.setFoodTotalPrice(foodVo.getPrice().multiply(BigDecimal.valueOf(cart.getQuantity())));
            orderItem.setOrderId(order.getId());
            orderItems.add(orderItem);
        }
        return orderItems;
    }

    private Order buildOrder(List<CartSDK> carts,Map<Integer, FoodVo> foodVoMap, CookbookSDK cookbookVo, OrderBo orderBo, Integer userId) {
        BigDecimal totalPrice = carts.stream().map(cart -> {
            FoodVo foodVo = foodVoMap.get(cart.getFoodId());
            return foodVo.getPrice().multiply(BigDecimal.valueOf(cart.getQuantity()));
        }).reduce(BigDecimal.ZERO, BigDecimal::add);
        Order order = new Order();
        order.setCookbookId(cookbookVo.getId());
        order.setCookbookType(orderBo.getCookbookType());
        order.setEatTime(cookbookVo.getEatTime());
        order.setOrderStatus(OrderStatusEnum.COMMITTED.getCode());
        order.setOrderType(orderBo.getOrderType());
        order.setRemark(orderBo.getRemark());
        order.setTotalPrice(totalPrice);
        order.setUserId(userId);
        return order;
    }

    /**
     * 修改订单状态
     *
     * @param orderId
     * @param orderStatus
     * @return
     */
    @Transactional
    @Override
    public Boolean updateOrderStatus(Integer orderId, OrderStatusEnum orderStatus) {

        LambdaUpdateWrapper<Order> updateWrapper = new UpdateWrapper<Order>().lambda()
                .set(Order::getOrderStatus, orderStatus).eq(Order::getId, orderId);
        return super.update(updateWrapper);
    }
}
